using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace RGuang.Kit
{
    public static class CopyKit<TIn, TOut>
    {
        #region --- 深拷贝 - 表达式树 ---
        private static readonly Func<TIn, TOut> cache = GetFunc();
        private static Func<TIn, TOut> GetFunc()
        {
            ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
            List<MemberBinding> memberBindingList = new List<MemberBinding>();

            // 拷贝所有非静态的属性
            PropertyInfo[] properties = typeof(TOut).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            for (int i = 0; i < properties.Length; i++)
            {
                var item = properties[i];
                if (!item.CanWrite) continue;
                var property = typeof(TIn).GetProperty(item.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                if (property != null && property.CanRead)
                {
                    MemberExpression propertyExpression = Expression.Property(parameterExpression, property);
                    MemberBinding memberBinding = Expression.Bind(item, propertyExpression);
                    memberBindingList.Add(memberBinding);
                }
            }

            // 拷贝所有非静态的字段
            FieldInfo[] fields = typeof(TOut).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            for (int i = 0; i < fields.Length; i++)
            {
                var item = fields[i];
                var field = typeof(TIn).GetField(item.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                if (field != null)
                {
                    MemberExpression fieldExpression = Expression.Field(parameterExpression, field);
                    MemberBinding memberBinding = Expression.Bind(item, fieldExpression);
                    memberBindingList.Add(memberBinding);
                }
            }

            MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
            Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, parameterExpression);

            return lambda.Compile();
        }

        /// <summary>
        /// 深拷贝 - 表达式树
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static TOut CopyObject(TIn obj)
        {
            if (obj == null) throw new ArgumentNullException(nameof(obj));
            return cache(obj);
        }

        #endregion
    }


}

